#!/usr/bin/python

"""

Adder Compiler

"""

from __future__ import division, print_function
from optparse import OptionParser
import math
import sys
import glob
import os
import subprocess
from adder import Adder, Bite

# List of bytecode command mappings.
bytecode_command_map = {
		"NEWC": "?do_NEWC@@YAXHPBD@Z",
		"NEWA": "?do_NEWA@@YAXH@Z",
		"NEWS": "?do_NEWS@@YAXHPBD@Z",
		"NEWN": "?do_NEWN@@YAXHM@Z",
		"RMOVV": "?do_RMOVV@@YAXHH@Z",
		"RMOVA": "?do_RMOVA@@YAXHHM@Z",
		"RMOVP": "?do_RMOVP@@YAXHPBDH@Z",
		"MOVV": "?do_MOVV@@YAXHH@Z",
		"MOVA": "?do_MOVA@@YAXHHM@Z",
		"MOVP": "?do_MOVP@@YAXHPBDH@Z",
		"DMOVV": "?do_DMOVV@@YAXHH@Z",
		"DMOVA": "?do_DMOVA@@YAXHHM@Z",
		"DDMOVA": "?do_DDMOVA@@YAXHHH@Z",
		"DRMOVA": "?do_DRMOVA@@YAXHHM@Z",
		"DRMOVP": "?do_DRMOVP@@YAXHPBDH@Z",
		"DDRMOVA": "?do_DDRMOVA@@YAXHHH@Z",
		"REFA": "?do_REFA@@YAXHHM@Z",
		"REFP": "?do_REFP@@YAXHPBDH@Z",
		"CMPV": "?do_CMPV@@YAXHHPBD@Z",
		"CMPV": "?do_CMPV@@YAXHHM@Z",
		"CMPR": "?do_CMPR@@YAXHHH@Z",
		"BVS": "?do_BVS@@YAXPBDH@Z",
		"BVNS": "?do_BVNS@@YAXPBDH@Z",
		"BRS": "?do_BRS@@YAXPBDH@Z",
		"BRNS": "?do_BRNS@@YAXPBDH@Z",
		"BT": "?do_BT@@YAXPBDH@Z",
		"BF": "?do_BF@@YAXPBDH@Z",
		"BU": "?do_BU@@YAXPBD@Z",
		"BERR": "?do_BERR@@YAXPBDHH@Z",
		"BRK": "?do_BRK@@YAXPBD@Z",
		"CLR": "?do_CLR@@YAXH@Z",
		"CLRV": "?do_CLRV@@YAXH@Z",
		"LBL": "?do_LBL@@YAXPBD@Z",
		"JMPR": "?do_JMPR@@YAXM@Z",
		"JMPA": "?do_JMPA@@YAXM@Z",
		"JMPL": "?do_JMPL@@YAXPBD@Z",
		"RET": "?do_RET@@YAXH@Z",
		"S_ADD": "?do_ADD@@YAXHPBD@Z",
		"ADD": "?do_ADD@@YAXHH@Z",
		"F_ADD": "?do_ADD@@YAXHM@Z",
		"SUB": "?do_SUB@@YAXHH@Z",
		"F_SUB": "?do_SUB@@YAXHM@Z",
		"MUL": "?do_MUL@@YAXHH@Z",
		"F_MUL": "?do_MUL@@YAXHM@Z",
		"DIV": "?do_DIV@@YAXHH@Z",
		"F_DIV": "?do_DIV@@YAXHM@Z",
		"POW": "?do_POW@@YAXHH@Z",
		"F_POW": "?do_POW@@YAXHM@Z",
		"EQ": "?do_EQ@@YAXHH@Z",
		"NE": "?do_NE@@YAXHH@Z",
		"LT": "?do_LT@@YAXHH@Z",
		"LE": "?do_LE@@YAXHH@Z",
		"GT": "?do_GT@@YAXHH@Z",
		"GE": "?do_GE@@YAXHH@Z",
		"CALLF": "?do_CALLF@@YAXPBDHH@Z",
		"CALLC": "?do_CALLC@@YAXPBDHHH@Z",
		"ERR": "?do_ERR@@YAXMPBD@Z",
	}

# List of bytecode mappings and their argument types.
REGISTER = 0
VARIABLE = 1
STRING = 2
NUMBER = 3
CLASS = 4
INDEX = 5
PROPERTY = 6
LABEL = 7
FUNCTION = 8
ANY = 9

bytecode_argument_map = {
		"NEWC":     (REGISTER, CLASS),
		"NEWA":     (REGISTER,),
		"NEWS":     (REGISTER, STRING),
		"NEWN":     (REGISTER, NUMBER),
		"RMOVV":    (REGISTER, VARIABLE),
		"RMOVA":    (REGISTER, VARIABLE, INDEX),
		"RMOVP":    (REGISTER, PROPERTY, REGISTER),
		"MOVV":     (REGISTER, VARIABLE),
		"MOVA":     (REGISTER, VARIABLE, INDEX),
		"MOVP":     (REGISTER, PROPERTY, REGISTER),
		"DMOVV":    (REGISTER, REGISTER),
		"DMOVA":    (REGISTER, REGISTER, INDEX),
		"DDMOVA":   (REGISTER, REGISTER, REGISTER),
		"DRMOVA":   (REGISTER, REGISTER, INDEX),
		"DRMOVP":   (REGISTER, PROPERTY, REGISTER),
		"DDRMOVA":  (REGISTER, REGISTER, REGISTER),
		"REFA":     (REGISTER, VARIABLE, INDEX),
		"REFP":     (REGISTER, PROPERTY, REGISTER),
		"CMPV":     (REGISTER, REGISTER, ANY),
		"CMPR":     (REGISTER, REGISTER, REGISTER),
		"BVS":      (LABEL, VARIABLE),
		"BVNS":     (LABEL, VARIABLE),
		"BRS":      (LABEL, REGISTER),
		"BRNS":     (LABEL, REGISTER),
		"BT":       (LABEL, REGISTER),
		"BF":       (LABEL, REGISTER),
		"BU":       (LABEL,),
		"BERR":     (LABEL, REGISTER, REGISTER),
		"BRK":      (LABEL,),
		"CLR":      (REGISTER,),
		"CLRV":     (VARIABLE,),
		"LBL":      (LABEL,),
		"JMPR":     (NUMBER,),
		"JMPA":     (NUMBER,),
		"JMPL":     (LABEL,),
		"RET":      (REGISTER,),
		"ADD":      (REGISTER,ANY),
		"SUB":      (REGISTER,ANY),
		"MUL":      (REGISTER,ANY),
		"DIV":      (REGISTER,ANY),
		"POW":      (REGISTER,ANY),
		"EQ":       (REGISTER,REGISTER),
		"NE":       (REGISTER,REGISTER),
		"LT":       (REGISTER,REGISTER),
		"LE":       (REGISTER,REGISTER),
		"GT":       (REGISTER,REGISTER),
		"GE":       (REGISTER,REGISTER),
		"CALLF":    (FUNCTION,REGISTER,REGISTER),
		"CALLC":    (FUNCTION,REGISTER,REGISTER,REGISTER),
		"ERR":      (NUMBER,STRING),
	}

class Compiler():
	def compile(self, parsed_args):
		norestart = False
		options, args = parsed_args
		
		# Check for input parameter.
		if (len(args) < 1):
			print("ERROR: You must provide an input filename (Bite file).")
			return
		else:
			self.opt_input = args[0]
		
		# Store the optional parameters
		self.opt_asm = options.asm
		self.opt_output = options.output
		self.opt_release = options.release
		self.opt_stepbystep = options.stepbystep
		self.opt_extensions = options.extensions
		self.opt_lib_core = options.lib_core
		self.opt_lib_ext = options.lib_ext
		
		try:
			sys.stdout.write("Reading file... ")
			f = open(self.opt_input)
			self.bytecode = f.read()
			f.close()
			sys.stdout.write("done.\n")
		except IOError:
			print("Invalid filename entered.")
			return
		
		# Get an output executable name before running the functions below.
		n = self.opt_input.rfind(".")
		if (self.opt_asm == None):
			if (n == -1):
				asmfilename = self.opt_input + ".asm"
			else:
				asmfilename = self.opt_input[:n] + ".asm"
		else:
			asmfilename = self.opt_asm
		
		if (self.opt_output == None):
			if (n == -1):
				exefilename = self.opt_input + ".exe"
			else:
				exefilename = self.opt_input[:n] + ".exe"
		else:
			exefilename = self.opt_output
		
		# Before we generate our assembly, check to see if all
		# our extensions exist.
		m = "debug"
		if (self.opt_release):
			m = "release"
		for i in self.opt_extensions:
			if (not os.path.exists(os.path.join(self.opt_lib_ext, i + "_" + m + ".lib"))):
				print("ERROR: Unable to locate extension library " + i + " at " + os.path.join(self.opt_lib_ext, i + "_" + m + ".lib"))
				return
		
		if (self.opt_stepbystep):
			m += "_ss"
		# Check the core libraries exist.
		if (not os.path.exists(os.path.join(self.opt_lib_core, "core_" + m + ".lib"))):
			print("ERROR: Unable to locate core library " + i + " at " + os.path.join(self.opt_lib_ext, "core_" + m + ".lib"))
			return
		
		# Check the required MASM and LINK programs exist.
		if self.joinLists([glob.glob(x + "\\ml.exe") for x in os.environ["PATH"].split(";")]) == 0:
			print("ERROR: Unable to locate ml (Microsoft Assembler) in PATH.  Make sure you run this program from the Visual Studio 2008 Command Prompt.")
			return
		elif self.joinLists([glob.glob(x + "\\mt.exe") for x in os.environ["PATH"].split(";")]) == 0:
			print("ERROR: Unable to locate mt (Manifest Tool) in PATH.  Make sure you run this program from the Visual Studio 2008 Command Prompt.")
			return
		elif self.joinLists([glob.glob(x + "\\link.exe") for x in os.environ["PATH"].split(";")]) == 0:
			print("ERROR: Unable to locate link (Microsoft Linker) in PATH.  Make sure you run this program from the Visual Studio 2008 Command Prompt.")
			return
		
		# Now generate the .ASM file.
		self.generateASM(asmfilename)
		
		# Compile the ASM and link against the specified extension libraries.
		if not (self.linkAndCompileASM(asmfilename, self.opt_extensions, exefilename, self.opt_release)):
			return
		
		# Show output information
		print()
		print("----- COMPILATION SUCCESS -----")
		print("Assembly file has been saved to: " + asmfilename)
		print("Compiled executable has been saved to: " + exefilename)
	
	def joinLists(self, l):
		# Required for path searching
		a = list()
		for i in l:
			for b in i:
				a.append(b)
		return a
	
	def linkAndCompileASM(self, asm, extensions, exe, release_mode):
		m = "debug"
		if (self.opt_release):
			m = "release"
		
		l = list()
		for i in self.opt_extensions:
			l.append(os.path.join(self.opt_lib_ext, i + "_" + m + ".lib"))
		
		if (self.opt_stepbystep):
			m += "_ss"
		
		a = [
				"ml",
				asm,
				"/nologo",
				"/link",
				"/nologo",
				"/entry:main",
				"/out:" + exe,
				"msvcrt.lib",
				"kernel32.lib",
				"user32.lib",
				"gdi32.lib",
				"winspool.lib",
				"comdlg32.lib",
				"advapi32.lib",
				"shell32.lib",
				"ole32.lib",
				"oleaut32.lib",
				"uuid.lib",
				"odbc32.lib",
				"odbccp32.lib",
				os.path.join(self.opt_lib_core, "core_" + m + ".lib")
			]
		a.extend(l)
		retcode = subprocess.call(a);
		if (retcode != 0):
			print("ERROR: A problem occurred while assembling the application.  See above for details.")
			return False
		
		retcode = subprocess.call([
									"mt",
									"-nologo",
									"-manifest",
									exe+".manifest",
									"-outputresource:"+exe+";1"
									]);
		if (retcode != 0):
			print("ERROR: A problem occurred while embedding the manifest.  See above for details.")
			return False
		
		# Now cleanup.
		for i in glob.glob("*.manifest"):
			try:
				os.remove(i)
			except:
				print("INFO: Unable to cleanup " + i + ".")
		for i in glob.glob("*.obj"):
			try:
				os.remove(i)
			except:
				print("INFO: Unable to cleanup " + i + ".")
		
		try:
			os.remove("mllink$.lnk")
		except:
			print("INFO: Unable to cleanup mllink$.lnk.")
			
		return True
	
	def allocateRegister(self, register_name):
		try:
			return self.register_map.index(register_name)
		except ValueError:
			self.register_map.append(register_name)
			return len(self.register_map) - 1
	
	def freeRegister(self, register_map, register_name):
		# no longer used since engine change (though compiler
		# will still call this function)
		
		#try:
		#	if (register_map.index(register_name) == len(register_map) - 1):
		#		del register_map[register_map.index(register_name)]
		#		for i in range(len(register_map) - 1,0,-1):
		#			if (register_map[i] == None):
		#				del register_map[i]
		#			else:
		#				break
		#		if (len(register_map) == 1 and register_map[0] == None):
		#			del register_map[0]
		#	else:
		#		register_map[register_map.index(register_name)] = None
		#except ValueError:
		#	pass
		pass
	
	def valueIsRegister(self, value):
		value = str(value).strip()
		if (value.startswith('"') and value.endswith('"')):
			return False
		else:
			try:
				a = float(value)
				return False
			except:
				return True
	
	def allocateVariable(self, variable_name):
		try:
			return self.variable_map.index(variable_name)
		except ValueError:
			self.variable_map.append(variable_name)
			return len(self.variable_map) - 1
	
	def generateASM(self, filename):
		# Load the bytecode's functions, classes and main bytecode.
		interpreter = Adder();
		bytecode, functions, classes, imports = interpreter.loadBytecode(self.bytecode.split("\n"))
		del interpreter
		bytecode = bytecode.split("\n")
		
		# Detect any additional extensions that may be required.
		m = "debug"
		if (self.opt_release):
			m = "release"
		
		for i in imports:
			if (os.path.exists(os.path.join(self.opt_lib_ext, i + "_" + m + ".lib"))):
				self.opt_extensions.append(i)
				print("INFO: Automatically detected extension " + i + " is required.")
			else:
				print("WARN: Unable to find import " + i + " as extension.  Program may not work correctly.")
		
		# Some bytecode commands contain strings
		# which we need to be aware of when splitting.
		# This dictionary specifies the bytecode
		# command and the number of parameters it has.
		bytecode_splits = {
				"ADD": 1,
				"NEWS": 1
			};
		
		# We need to keep a list of converted registers and variables
		self.register_map = list()
		self.variable_map = list()
		
		# Since all of our static values need to
		# be mapped to a data section in the ASM
		# file, we keep a value_map.
		value_map = {}
		self.v_count = 0
		
		# Counters for various things.
		self.function_skip_counter = 0
		self.function_required_map = list()
		self.cfunction_required_map = dict()
		
		# Convert the main bytecode area.
		asm_code = self.bytecodeToAssembly(bytecode, value_map, bytecode_splits)
		
		
		
		# Now we need to handle all of the class function definitions.
		asm_functions = ""
		asm_functions_registration = ""
		asm_classes_registration = ""
		for c in classes:
			c_name = "C" + str(self.v_count).rjust(8, "0")
			value_map[c_name] = classes[c].type
			self.v_count += 1
			
			asm_classes_registration += """
	push		OFFSET """ + c_name + """
	call		?engine_register_class@@YAXPBD@Z
	add			esp, 4
"""
			if (c not in self.cfunction_required_map):
				self.cfunction_required_map[c] = list()
			
			self.cfunction_required_map[c] = self.makeListUnique(self.cfunction_required_map[c])
			for i in classes[c].functions:#self.cfunction_required_map[c]:
				if (i in classes[c].functions):
					register_mappings = list()
				
					# User-defined function.
					asm_functions += """_class_func_""" + c + """_?_""" + i + """ PROC
	push        ebp  
	mov         ebp,esp 
	sub         esp,0C0h 
	push        ebx  
	push        esi  
	push        edi  
	lea         edi,[ebp-0C0h] 
	mov         ecx,30h 
	mov         eax,0CCCCCCCCh 
	rep stos    dword ptr es:[edi]
	
""" + self.bytecodeToAssembly(classes[c].functions[i].code.split("\n"), value_map, bytecode_splits) + """
	
	call        ?engine_scope_cleanup@@YAXXZ
	
	pop         edi  
	pop         esi  
	pop         ebx  
	add         esp,0C0h 
	cmp         ebp,esp
	mov         esp,ebp 
	pop         ebp  
	ret
_class_func_""" + c + """_?_""" + i + """ ENDP
"""
				
					# Now we need to generate the code to register our function
					# with the engine (so that it knows it can jump).
					for x in classes[c].functions[i].args:
						v = self.allocateVariable(x)
						
						asm_functions_registration += """
	push		""" + str(v) + """
	call		?engine_register_function_argument@@YAXI@Z
	add			esp, 4
	"""
					v_name = "V" + str(self.v_count).rjust(8, "0")
					value_map[v_name] = classes[c].functions[i].name
					self.v_count += 1
					
					asm_functions_registration += """
	push		OFFSET _class_func_""" + c + """_?_""" + i + """
	push		OFFSET """ + c_name + """
	push		OFFSET """ + v_name + """
	call		?engine_register_function_class_call@@YAXPBD0PAX@Z
	add			esp, 4
"""
				else:
					# Builtin
					asm_functions += """_class_func_""" + c + """_?_""" + i + """ PROC
	ret
_class_func_""" + c + """_?_""" + i + """ ENDP
"""
		
		# Now we need to handle all of the function definitions.
		self.function_required_map = self.makeListUnique(self.function_required_map)
		for i in self.function_required_map:
			if (i in functions and i not in classes):
				register_mappings = list()
				
				# User-defined function.
				asm_functions += """_global_func_""" + i + """ PROC
	push        ebp  
	mov         ebp,esp 
	sub         esp,0C0h 
	push        ebx  
	push        esi  
	push        edi  
	lea         edi,[ebp-0C0h] 
	mov         ecx,30h 
	mov         eax,0CCCCCCCCh 
	rep stos    dword ptr es:[edi]
	
""" + self.bytecodeToAssembly(functions[i].code.split("\n"), value_map, bytecode_splits) + """
	
	call        ?engine_scope_cleanup@@YAXXZ
	
	pop         edi  
	pop         esi  
	pop         ebx  
	add         esp,0C0h 
	cmp         ebp,esp
	mov         esp,ebp 
	pop         ebp  
	ret
_global_func_""" + i + """ ENDP
"""
				
				# Now we need to generate the code to register our function
				# with the engine (so that it knows it can jump).
				for x in functions[i].args:
					v = self.allocateVariable(x)
					
					asm_functions_registration += """
	push		""" + str(v) + """
	call		?engine_register_function_argument@@YAXI@Z
	add			esp, 4
"""
				v_name = "V" + str(self.v_count).rjust(8, "0")
				value_map[v_name] = functions[i].name
				self.v_count += 1
				
				asm_functions_registration += """
	push		OFFSET """ + v_name + """
	call		?engine_register_function_call@@YAXPBD@Z
	add			esp, 4
"""
			else:
				# Builtin
				asm_functions += """_global_func_""" + i + """ PROC
	ret
_global_func_""" + i + """ ENDP
"""
		
		# Generate our data code.
		asm_data = ""
		for nindex, index in enumerate(value_map):
			if (isinstance(value_map[index], float)):
				asm_data += "	" + index + ("	REAL4	%ff" % value_map[index]) + """
"""
			else:
				s = ('"' + value_map[index] + '", 0').replace('"", ','')
				asm_data += "	" + index + "	BYTE	" + s + """
"""
		
		# Generate our extension area.
		extension_symbols = ""
		extension_load = ""
		for i in self.opt_extensions:
			extension_symbols += "EXTERNDEF ?InitalizeBuiltins@"+i+"@@YAXPAVAdderBytecodeEngine@@@Z:NEAR\n"
			extension_load += """	push		OFFSET ?InitalizeBuiltins@"""+i+"""@@YAXPAVAdderBytecodeEngine@@@Z
	call		?engine_builtin_load_library@@YAXP6AXPAVAdderBytecodeEngine@@@Z@Z
	add			esp, 4
"""
		
		# Set the debug switch
		debug_switch = ""
		if self.opt_stepbystep:
			debug_switch = "	call        ?debug_on@@YAXXZ"
		
		# Now write our file.
		asm = """; This file was automatically generated by the Adder
; Compiler.  By linking this file against the Adder Engine static library
; and msvcrt.lib, you can produce a native executable version of your
; Adder code.
.586
.MODEL FLAT

EXTERNDEF _acrtused:WORD
EXTERNDEF ?engine_init@@YAXII@Z:NEAR
EXTERNDEF ?engine_cleanup@@YAXXZ:NEAR
EXTERNDEF ?engine_builtin_load_library@@YAXP6AXPAVAdderBytecodeEngine@@@Z@Z:NEAR
EXTERNDEF ?engine_register_function_argument@@YAXI@Z:NEAR
EXTERNDEF ?engine_register_function_call@@YAXPBD@Z:NEAR
EXTERNDEF ?engine_register_function_class_call@@YAXPBD0PAX@Z:NEAR
EXTERNDEF ?engine_register_class@@YAXPBD@Z:NEAR
EXTERNDEF ?engine_get_assembly_jump_required@@YA_NXZ:NEAR
EXTERNDEF ?engine_get_assembly_class_jump_position@@YAPAXXZ:NEAR
EXTERNDEF ?engine_get_is_breaking@@YA_NXZ:NEAR
EXTERNDEF ?engine_scope_cleanup@@YAXXZ:NEAR
EXTERNDEF ?debug_on@@YAXXZ:NEAR
""" + extension_symbols + """

INCLUDE include/bytecodes.inc

.STACK
.DATA
""" + asm_data + """
.CODE

main PROC
	; Initialize the application.
	push        ebp  
	mov         ebp,esp 
	sub         esp,0C0h 
	push        ebx  
	push        esi  
	push        edi  
	lea         edi,[ebp-0C0h] 
	mov         ecx,30h 
	mov         eax,0CCCCCCCCh 
	rep stos    dword ptr es:[edi]
	
	; Initalize the engine.
	push		""" + str(len(self.register_map)) + """
	push		""" + str(len(self.variable_map)) + """
	call        ?engine_init@@YAXII@Z
	add			esp, 4
	
""" + debug_switch + """
	
""" + extension_load + """
	
	; Register our user-defined classes
	""" + asm_classes_registration.strip() + """
	
	; Register our user-defined functions
	""" + asm_functions_registration.strip() + """
	
	; Run the bytecode commands.
""" + asm_code + """
	; Cleanup.
	call        ?engine_cleanup@@YAXXZ
	xor         eax,eax
	
	pop         edi  
	pop         esi  
	pop         ebx  
	add         esp,0C0h 
	cmp         ebp,esp
	mov         esp,ebp 
	pop         ebp  
	ret
main ENDP
""" + asm_functions + """
END
"""
		
		# Now write it to file.
		f = open(filename, 'w')
		f.write(asm)
		f.close()
	
	def bytecodeToAssembly(self, bytecode, value_map, bytecode_splits):
		# Loop through all of the lines in the bytecode,
		# converting and storing the paramters in value_map
		# and generating the ASM calls as well.
		print("Processing...")
		l_count = 0
		asm_code = ""
		for i in bytecode:
			# Check for comments or interpreter
			# debug info.
			if (i.startswith("#") or i.startswith(";") or i.strip() == ""):
				continue
		
			# Split the line into the command and it's arguments.
			a = i.split(" ", 1)
			command = a[0]
			if (len(a) > 1):
				args = a[1]
			else:
				args = ""
			
			# Now split the arguments depending on the command.
			if (command in bytecode_splits):
				args = args.split(",", bytecode_splits[command])
			else:
				args = args.split(",")
				
			if (args[0].strip() == ""): args = [];
			
			# Check if the command is LBL or JMPL
			if (command == "LBL"):
				asm_code += args[0] + """:

"""
				continue
			elif (command == "JMPL"):
				asm_code += """	call		?engine_get_is_breaking@@YA_NXZ
	test		al, al
	jnz			""" + args[0] + """_SKIP
	; Returned true
	; Jump to our new location
	jmp			""" + args[0] + """
""" + args[0] + """_SKIP:
	; Returned false at this point

"""
				continue
			
			# REGISTER CONVERSION:  Go through the bytecode_argument_map and find out what
			# arguments are registers / variables, then convert them appropriately.
			if (command in bytecode_argument_map):
				r_vars = bytecode_argument_map[command]
				
				a = 0
				for z in r_vars:
					if (z == REGISTER or (z == ANY and self.valueIsRegister(args[a].strip()))):
						b = self.allocateRegister(args[a].strip())
						args[a] = "_REGISTER " + str(b)
					elif (z == VARIABLE):
						b = self.allocateVariable(args[a].strip())
						args[a] = "_VARIABLE " + str(b)
					a += 1
			else:
				print("  " + i.ljust(40) + " -> FAILED")
				raise Exception("Unable to detect appropriate arguments for bytecode command: " + command)
			
			b = list()
			for z in args:
				if (z.startswith("_REGISTER ") or z.startswith("_VARIABLE ")):
					b.append(z[10:].strip())
				else:
					b.append(z.strip())
			print("  " + i.ljust(40) + " -> " + command + " " + ", ".join(b))
			
			# Hold a map of all of the arguments (with their new
			# names) to generate the ASM code.
			v_map = list()
			
			# Now take each of the arguments and put them into the
			# value map.
			V_NUMBER = 0
			V_REGISTER = 1
			V_VARIABLE = 2
			V_OTHER = 3
			
			for b in args:
				v_name = "V" + str(self.v_count).rjust(8, "0")
				if (b.strip() == '""'):
					value_map[v_name] = '", 34, 34, "'
					v_map.append((v_name, V_OTHER))
				elif (b.strip().startswith('"') and b.strip().endswith('"')):
					value_map[v_name] = b.strip().replace('"','", 34, "')
					v_map.append((v_name, V_OTHER))
				else:
					# Attempt to convert to a number.
					try:
						value_map[v_name] = float(b)
						v_map.append((v_name, V_NUMBER))
					except:
						# Or it's the name of a register/variable/label/etc..
						if (b.strip().startswith("_REGISTER ")):
							# It's a converted register.
							c = str(b.strip()[10:])
							v_map.append((c, V_REGISTER))
						elif (b.strip().startswith("_VARIABLE ")):
							# It's a converted variable.
							c = str(b.strip()[10:])
							v_map.append((c, V_VARIABLE))
						else:
							# It's something else..
							value_map[v_name] = b.strip()
							v_map.append((v_name, V_OTHER))
				
				self.v_count += 1
			
			# Reverse the value map, because ASM loads it's variables
			# in reverse.
			v_map.reverse()
			
			# Now generate our wonderful ASM code.
			for v in v_map:
				if (v[1] == V_NUMBER):
					# Floating-point integers are handled specially.
					asm_code += """	push		ecx
	fld			dword ptr [""" + v[0] + """] 
	fstp		dword ptr [esp]
"""
				elif (v[1] == V_REGISTER):
					asm_code += """	push		""" + v[0] + """
"""
				elif (v[1] == V_VARIABLE):
					asm_code += """	push		""" + v[0] + """
"""
				else:
					asm_code += """	push		OFFSET """ + v[0] + """
"""
			
			# Now add our function call.
			if ("F_" + command in bytecode_command_map and v_map[0][1] == V_NUMBER):
				asm_code += """	call		""" + bytecode_command_map["F_" + command] + """
	add			esp, """ + str(len(v_map)*4) + """

"""
			elif ("S_" + command in bytecode_command_map and v_map[0][1] == V_OTHER):
				asm_code += """	call		""" + bytecode_command_map["S_" + command] + """
	add			esp, """ + str(len(v_map)*4) + """

"""
			elif (command == "CALLF"):
				# Functions have special calling mechanisms.
				f_name = "CFSKIP_" + str(self.function_skip_counter).rjust(8, "0")
				try:
					g = self.function_required_map.index(value_map[v_map[len(v_map) - 1][0]]);
				except ValueError:
					self.function_required_map.append(value_map[v_map[len(v_map) - 1][0]]);
				self.function_skip_counter += 1
				asm_code += """	call		""" + bytecode_command_map[command] + """
	add			esp, """ + str(len(v_map)*4) + """
	
	call		?engine_get_assembly_jump_required@@YA_NXZ
	test		al, al
	jz			""" + f_name + """
	; Returned true
	; Call our internal function.  This assembly is hardcoded
	; and generated at ASM generation time (the label below
	; matches a label that actually has all of the bytecode commands
	; in it).
	call		_global_func_""" + value_map[v_map[len(v_map) - 1][0]] + """
""" + f_name + """:
	; Returned false at this point

"""
			elif (command == "CALLC"):
				# Functions have special calling mechanisms.
				f_name = "CFSKIP_" + str(self.function_skip_counter).rjust(8, "0")
				#try:
				#	g = self.function_required_map.index(value_map[v_map[len(v_map) - 1][0]]);
				#except ValueError:
				#	self.function_required_map.append(value_map[v_map[len(v_map) - 1][0]]);
				self.function_skip_counter += 1
				asm_code += """	call		""" + bytecode_command_map[command] + """
	add			esp, """ + str(len(v_map)*4) + """
	
	call		?engine_get_assembly_jump_required@@YA_NXZ
	test		al, al
	jz			""" + f_name + """
	; Returned true
	call		?engine_get_assembly_class_jump_position@@YAPAXXZ
	jmp			eax
""" + f_name + """:
	; Returned false at this point

"""
			else:
				asm_code += """	call		""" + bytecode_command_map[command] + """
	add			esp, """ + str(len(v_map)*4) + """

"""
		
		return asm_code
	
	def makeListUnique(self, seq, idfun=None):  
		# order preserving 
		if idfun is None: 
			def idfun(x): return x 
		seen = {} 
		result = [] 
		for item in seq: 
			marker = idfun(item) 
			# in old Python versions: 
			# if seen.has_key(marker) 
			# but in new ones: 
			if marker in seen: continue 
			seen[marker] = 1 
			result.append(item) 
		return result

#### MAIN APPLICATION ####

# Get a list of extension libraries to link against.
extensions = list()
parser = OptionParser(usage="usage: %prog [options] INPUT", version="Adder Compiler 1.0")
parser.add_option("-a", "--asm", dest="asm",
				  help="Output the assembly to FILE", metavar="FILE")
parser.add_option("-o", "--out", dest="output",
				  help="Write the native application (.exe) to FILE", metavar="FILE")
parser.add_option("-e", "--extension", dest="extensions", action="append", default=list(),
				  help="Add an extension to your native executable", metavar="EXT")
parser.add_option("-D", "--debug", dest="stepbystep", action="store_true", default=False,
				  help="Enable step-by-step debugging", metavar="FILE")
parser.add_option("--vc-debug", dest="release", action="store_false", default=True,
				  help="Enable Visual Studio C++ debugging symbols", metavar="FILE")
parser.add_option("--core-dir", dest="lib_core", default="lib_core/",
				  help="Use the core libraries in DIR", metavar="DIR")
parser.add_option("--ext-dir", dest="lib_ext", default="lib_ext/",
				  help="Use extensions from DIR", metavar="DIR")

p = parser.parse_args()

print("Welcome to the Adder Compiler.")
com = Compiler()
com.compile(p)